#include "routeguide.h"

#include <QDebug>
#include <QThread>
#include <QMessageBox>

std::shared_ptr<RouteGuideClient> g_client;

void RouteGuideClient::GetFeature()
{
    Point point;
    Feature feature;
    point = MakePoint(409146138, -746188906);
    GetOneFeature(point, &feature);
    point = MakePoint(0, 0);
    GetOneFeature(point, &feature);
}

void RouteGuideClient::ListFeatures()
{
    routeguide::Rectangle rect;
    Feature feature;
    ClientContext context;

    rect.mutable_lo()->set_latitude(400000000);
    rect.mutable_lo()->set_longitude(-750000000);
    rect.mutable_hi()->set_latitude(420000000);
    rect.mutable_hi()->set_longitude(-730000000);
    qDebug() << "Looking for features between 40, -75 and 42, -73"
              ;

    std::unique_ptr<ClientReader<Feature> > reader(
        stub_->ListFeatures(&context, rect));
    while (reader->Read(&feature)) {
        qDebug() << "Found feature called "
                  << feature.name().c_str() << " at "
                  << feature.location().latitude()/kCoordFactor_ << ", "
                  << feature.location().longitude()/kCoordFactor_ ;

        //QMessageBox::critical(nullptr, "assfsf","sdgsgdsg");
    }
    Status status = reader->Finish();
    if (status.ok()) {
        qDebug() << "ListFeatures rpc succeeded." ;
    } else {
        qDebug() << "ListFeatures rpc failed." ;
    }

    qDebug() << "channel state" << channel_->GetState(true);
}

void RouteGuideClient::AsyncListFeatures()
{
    routeguide::Rectangle rect;
    Feature feature;

    rect.mutable_lo()->set_latitude(400000000);
    rect.mutable_lo()->set_longitude(-750000000);
    rect.mutable_hi()->set_latitude(420000000);
    rect.mutable_hi()->set_longitude(-730000000);
    qDebug() << "Looking for features between 40, -75 and 42, -73"
              ;

    Reader reader(stub_.get(), kCoordFactor_, rect);
    Status status = reader.Await();
    if (status.ok()) {
        qDebug() << "ListFeatures rpc succeeded.";
    } else {
        qDebug() << "ListFeatures rpc failed.";
    }


}

void RouteGuideClient::RecordRoute()
{
    Point point;
    RouteSummary stats;
    ClientContext context;
    const int kPoints = 10;
    unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();

    std::default_random_engine generator(seed);
    std::uniform_int_distribution<int> feature_distribution(
        0, feature_list_.size() - 1);
    std::uniform_int_distribution<int> delay_distribution(
        500, 1500);

    std::unique_ptr<ClientWriter<Point> > writer(
        stub_->RecordRoute(&context, &stats));
    for (int i = 0; i < kPoints; i++) {
        const Feature& f = feature_list_[feature_distribution(generator)];
        qDebug() << "Visiting point "
                  << f.location().latitude()/kCoordFactor_ << ", "
                  << f.location().longitude()/kCoordFactor_ ;
        if (!writer->Write(f.location())) {
            // Broken stream.
            break;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(
            delay_distribution(generator)));
    }
    writer->WritesDone();
    Status status = writer->Finish();
    if (status.ok()) {
        qDebug() << "Finished trip with " << stats.point_count() << " points\n"
                  << "Passed " << stats.feature_count() << " features\n"
                  << "Travelled " << stats.distance() << " meters\n"
                  << "It took " << stats.elapsed_time() << " seconds"
                  ;
    } else {
        qDebug() << "RecordRoute rpc failed." ;
    }
}

void RouteGuideClient::AsyncRecordRoute()
{
    Recorder recorder(stub_.get(), kCoordFactor_, &feature_list_);


//    RouteSummary stats;
//    Status status = recorder.Await(&stats);
//    if (status.ok()) {
//        qDebug() << "Finished trip with " << stats.point_count() << " points\n"
//                  << "Passed " << stats.feature_count() << " features\n"
//                  << "Travelled " << stats.distance() << " meters\n"
//                  << "It took " << stats.elapsed_time() << " seconds"
//                  ;
//    } else {
//        qDebug() << "RecordRoute rpc failed." ;
//    }

    QThread::sleep(5); //模拟后续业务数据来了才写
    recorder.NextWrite();
    qDebug() << "first NextWrite";

    //假如上面不等待，是全局的recorder,
    QThread::sleep(30);  //模拟程序在做其他了

}

void RouteGuideClient::RouteChat()
{
    ClientContext context;

    std::shared_ptr<ClientReaderWriter<RouteNote, RouteNote> > stream(
        stub_->RouteChat(&context));

    std::thread writer([stream]() {
        std::vector<RouteNote> notes{
                                     MakeRouteNote("First message", 0, 0),
                                     MakeRouteNote("Second message", 0, 1),
                                     MakeRouteNote("Third message", 1, 0),
                                     MakeRouteNote("Fourth message", 0, 0)};
        for (const RouteNote& note : notes) {
            qDebug() << "Sending message " << note.message().c_str()
                      << " at " << note.location().latitude() << ", "
                      << note.location().longitude() ;
            stream->Write(note);
        }
        stream->WritesDone();
    });

    RouteNote server_note;
    while (stream->Read(&server_note)) {
        qDebug() << "Got message " << server_note.message().c_str()
                  << " at " << server_note.location().latitude() << ", "
                  << server_note.location().longitude() ;
    }
    writer.join();
    Status status = stream->Finish();
    if (!status.ok()) {
        qDebug() << "RouteChat rpc failed." ;
    }
}

void RouteGuideClient::AsyncRouteChat()
{
    ClientContext context;

    std::shared_ptr<ClientReaderWriter<RouteNote, RouteNote> > stream(
        stub_->RouteChat(&context));

    std::thread writer([stream]() {
        std::vector<RouteNote> notes{MakeRouteNote("First message", 0, 0),
                                     MakeRouteNote("Second message", 0, 1),
                                     MakeRouteNote("Third message", 1, 0),
                                     MakeRouteNote("Fourth message", 0, 0)};
        for (const RouteNote& note : notes) {
            std::cout << "Sending message " << note.message() << " at "
                      << note.location().latitude() << ", "
                      << note.location().longitude() << std::endl;
            if(!stream->Write(note))
            {
                qDebug() << "AsyncRouteChat write fail";

                return;
            }
        }
        stream->WritesDone();
    });

    RouteNote server_note;
    while (stream->Read(&server_note)) {
        std::cout << "Got message " << server_note.message() << " at "
                  << server_note.location().latitude() << ", "
                  << server_note.location().longitude() << std::endl;
    }
    writer.join();
    Status status = stream->Finish();
    if (!status.ok()) {
        qDebug() << "AsyncRouteChat rpc failed.";
    }
}

bool RouteGuideClient::GetOneFeature(const Point &point, Feature *feature)
{
    ClientContext context;
    Status status = stub_->GetFeature(&context, point, feature);
    if (!status.ok()) {
        qDebug() << "GetFeature rpc failed." ;
        return false;
    }
    if (!feature->has_location()) {
        qDebug() << "Server returns incomplete feature." ;
        return false;
    }
    if (feature->name().empty()) {
        qDebug() << "Found no feature at "
                  << feature->location().latitude()/kCoordFactor_ << ", "
                  << feature->location().longitude()/kCoordFactor_ ;
    } else {
        qDebug() << "Found feature called " << feature->name().c_str()  << " at "
                  << feature->location().latitude()/kCoordFactor_ << ", "
                  << feature->location().longitude()/kCoordFactor_ ;
    }
    return true;
}

void RouteGuideClient::GetAsyncFeatureRet(Status status)
{
    qDebug() << "GetAsyncFeatureRet" << QThread::currentThreadId();
    bool ret;
    if (!status.ok()) {
        qDebug() << "GetAsyncFeatureRet GetFeature rpc failed." ;
        ret = false;
    } else if (!replay.has_location()) {
        qDebug() << "GetAsyncFeatureRet Server returns incomplete feature." ;
        ret = false;
    } else if (replay.name().empty()) {
        qDebug() << "GetAsyncFeatureRet Found no feature at "
                  << replay.location().latitude() / kCoordFactor_ << ", "
                  << replay.location().longitude() / kCoordFactor_;
        ret = true;
    } else {
        qDebug() << "GetAsyncFeatureRet Found feature called " << replay.name().c_str() << " at "
                  << replay.location().latitude() / kCoordFactor_ << ", "
                  << replay.location().longitude() / kCoordFactor_;
        ret = true;
    }

}

bool RouteGuideClient::GetOneAsyncFeature() {
    ClientContext context;

    Point point;
    point = MakePoint(409146138, -746188906);


    std::function<void(Status)> m_asyncF = std::bind(&RouteGuideClient::GetAsyncFeatureRet, this, std::placeholders::_1);
    stub_->async()->GetFeature(
        &context, &point, &replay,
        std::move(m_asyncF));

//    m_func = [ this](Status status) {
//        bool ret;
//        if (!status.ok()) {
//            qDebug() << "GetFeature rpc failed." ;
//            ret = false;
//        } else if (!replay.has_location()) {
//            qDebug() << "Server returns incomplete feature." ;
//            ret = false;
//        } else if (replay.name().empty()) {
//            qDebug() << "Found no feature at "
//                     << replay.location().latitude() / kCoordFactor_ << ", "
//                     << replay.location().longitude() / kCoordFactor_
//                ;
//            ret = true;
//        } else {
//            qDebug() << "Found feature called " << replay.name().c_str() << " at "
//                     << replay.location().latitude() / kCoordFactor_ << ", "
//                     << replay.location().longitude() / kCoordFactor_
//                ;
//            ret = true;
//        }

//    };
//    stub_->async()->GetFeature(
//        &context, &point, &replay, m_func);

//    bool result;
//    std::mutex mu;
//    std::condition_variable cv;
//    bool done = false;
//    stub_->async()->GetFeature(
//        &context, &point, &replay,
//        [&result, &mu, &cv, &done,  this](Status status) {
//            bool ret;
//            if (!status.ok()) {
//                qDebug() << "GetFeature rpc failed." ;
//                ret = false;
//            } else if (!replay.has_location()) {
//                qDebug() << "Server returns incomplete feature." ;
//                ret = false;
//            } else if (replay.name().empty()) {
//                qDebug() << "Found no feature at "
//                          << replay.location().latitude() / kCoordFactor_ << ", "
//                          << replay.location().longitude() / kCoordFactor_
//                          ;
//                ret = true;
//            } else {
//                qDebug() << "Found feature called " << replay.name().c_str() << " at "
//                          << replay.location().latitude() / kCoordFactor_ << ", "
//                          << replay.location().longitude() / kCoordFactor_
//                          ;
//                ret = true;
//            }
//            std::lock_guard<std::mutex> lock(mu);
//            result = ret;
//            done = true;
//            cv.notify_one();
//        });
//    std::unique_lock<std::mutex> lock(mu);
//    cv.wait(lock, [&done] { return done; });

    QThread::sleep(20);

    return true;
}



void RouteGuideClientTest()
{
    std::string db = routeguide::GetDbFileContent(0, nullptr);
    g_client = std::make_shared<RouteGuideClient>(
        grpc::CreateChannel("127.0.0.1:50051",
                            grpc::InsecureChannelCredentials()),
        db);

    qDebug() << "main" << QThread::currentThreadId();
    //qDebug() << "-------------- GetASYNCFeature --------------" ;
    //g_client->GetOneAsyncFeature();

    //qDebug() << "-------------- GetFeature --------------" ;
    //guide.GetFeature();


    g_client->AsyncListFeatures();
    qDebug() << "-------------- AsyncListFeatures  --------------" ;

    //qDebug() << "-------------- ListFeatures --------------" ;
    //g_client->ListFeatures();


    //qDebug() << "-------------- RecordRoute --------------" ;
    //g_client->RecordRoute();
    //qDebug() << "-------------- AsyncRecordRoute --------------" ;
    //g_client->AsyncRecordRoute();


    //qDebug() << "-------------- RouteChat --------------" ;
    //guide.RouteChat();

    //qDebug() << "-------------- AsyncRouteChat --------------" ;
    //g_client->AsyncRouteChat();


    QThread::sleep(20);
    qDebug() << "-------------- finish  --------------" ;

}

Point MakePoint(long latitude, long longitude)
{
    Point p;
    p.set_latitude(latitude);
    p.set_longitude(longitude);
    return p;
}

Feature MakeFeature(const std::string &name, long latitude, long longitude)
{
    Feature f;
    f.set_name(name);
    f.mutable_location()->CopyFrom(MakePoint(latitude, longitude));
    return f;
}

RouteNote MakeRouteNote(const std::string &message, long latitude, long longitude)
{
    RouteNote n;
    n.set_message(message);
    n.mutable_location()->CopyFrom(MakePoint(latitude, longitude));
    return n;
}
